package com.wisdytech.common.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.wisdytech.common.constants.CommonConstants;
import com.wisdytech.linkdcs.log.service.ISysLogService;
import com.wisdytech.linkdcs.system.dao.ISysResourceDao;
import com.wisdytech.linkdcs.system.model.SysResource;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
@ConditionalOnClass
public class ShiroConfig {

    // cas server地址

    @Value("${cas.server-url-prefix}")
    private String casServerUrlPrefix;

    // Cas登录页面地址 http://192.168.102.38:8080/cas/login
    @Value("${cas.login-url}")
    private String casLoginUrl;
    // Cas登出页面地址 http://192.168.102.38:8080/cas/logout
    @Value("${cas.logout-url}")
    private String casLogoutUrl;
    // 当前工程对外提供的服务地址 http://192.168.102.38:8000/linkdcs

    @Value("${cas.client-url}")
    private String shiroServerUrlPrefix;

    // casFilter UrlPattern http://192.168.102.38:8000/linkdcs/index
    @Value("${cas.filter-url-pattern}")
    private String casFilterUrlPattern;
    // 登录地址 http://192.168.102.38:8080/cas/login?service=http://192.168.102.38:8000/linkdcs/index
    @Value("${cas.client-login-url}")
    private String loginUrl;
    // 登出地址（casserver启用service跳转功能，需在webapps\cas\WEB-INF\cas.properties文件中启用cas.logout.followServiceRedirects=true）
    @Value("${cas.client-logout-url}")
    private String logoutUrl;

    @Autowired
    private ISysResourceDao sysResourceDao;

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public FilterRegistrationBean casFilterRegistration(MyCasFilter casFilter) {
        FilterRegistrationBean bean = new FilterRegistrationBean(casFilter);
        bean.setEnabled(false);
        return bean;
    }

    @Bean
    public FilterRegistrationBean shiroLoginFilterRegistration(ShiroLoginFilter shiroLoginFilter) {
        FilterRegistrationBean bean = new FilterRegistrationBean(shiroLoginFilter);
        bean.setEnabled(false);
        return bean;
    }

    @Bean
    public FilterRegistrationBean casLogoutFilterRegistration(CasLogoutFilter casLogoutFilter) {
        FilterRegistrationBean bean = new FilterRegistrationBean(casLogoutFilter);
        bean.setEnabled(false);
        return bean;
    }


    /**
     * 一定要加，不加会出现多次验证的问题（具体原因不明）
     * @return FilterRegistrationBean
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {

        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new DelegatingFilterProxy("shiroFilter"));
        bean.addInitParameter("targetFilterLifecycle", "true");
        bean.setEnabled(true);
        bean.addUrlPatterns("/*");
        return bean;

    }




	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //自定义过滤
        Map<String, Filter> filters =shiroFilterFactoryBean.getFilters();
        /*
         *  添加casFilter到shiroFilter中，注意，casFilter需要放到shiroFilter的前面，
         *  从而保证程序在进入shiro的login登录之前就会进入单点认证
         */
        filters.put("casFilter", casFilter());
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl(logoutUrl);
        filters.put("logout",logoutFilter);
        filters.put("casLogout",casLogoutFilter());
        filters.put("log", systemLogFilter());
        filters.put("authc",shiroLoginFilter());
        shiroFilterFactoryBean.setFilters(filters);
        
        //url过滤
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
        
        //组态设计接口不拦截
        filterChainDefinitionMap.put("/equipment/configuration/design/**", "anon");
        filterChainDefinitionMap.put("/webSocketServer/**", "anon");

        filterChainDefinitionMap.put("/equipment/produce/**", "anon");
        filterChainDefinitionMap.put("/system/resource/getParentNode/get.mvc", "anon");

        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put(casFilterUrlPattern, "casLogout,casFilter");
        filterChainDefinitionMap.put("/3thExt/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/webjarslocator/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/image/**", "anon");
        
        //初始化所要拦截的资源
        //TODO 测试阶段不拦截资源
        EntityWrapper<SysResource> ew = new EntityWrapper<>();
        ew.eq("del_flag", CommonConstants.DEL_FLAG).eq("enabled","1");
        List<SysResource> sysResourceList = sysResourceDao.selectList(ew);
        for(SysResource sysResource : sysResourceList) {
        	if(StringUtils.isNotBlank(sysResource.getResCode())) {
        		filterChainDefinitionMap.put("/"+sysResource.getUri(), "perms["+sysResource.getResCode()+"]");
        	}
        }


        // 此处将logout页面设置为anon，而不是logout，因为logout被单点处理，而不需要再被shiro的logoutFilter进行拦截
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("/logout","logout");
        //<!-- 过滤链定义，从上向下顺序执行，一般将/**放在最为下边 -->:这是一个坑呢，一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "casLogout,authc");
        
        //针对页面url请求过滤
        filterChainDefinitionMap.put("/**/index", "log");
        filterChainDefinitionMap.put("/**/*.mvc", "log");



        
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 登录成功后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/system/user/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
	}

	@Bean
	public CasLogoutFilter casLogoutFilter() {
	    CasLogoutFilter casLogoutFilter = new CasLogoutFilter();
	    casLogoutFilter.setSessionManager(defaultWebSessionManager());
	    return casLogoutFilter;
    }


    /**
     * CAS过滤器
     * @return CasFilter
     */
    @Bean("casFilter")
    public MyCasFilter casFilter() {
        MyCasFilter casFilter = new MyCasFilter();
        casFilter.setName("casFilter");
        casFilter.setEnabled(true);
        // 登录失败后跳转的URL，也就是 Shiro 执行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer验证tiket
        casFilter.setFailureUrl(loginUrl);// 我们选择认证失败后再打开登录页面
        return casFilter;
    }


    @Bean
    public ShiroLoginFilter shiroLoginFilter() {
        return new ShiroLoginFilter();
    }


	@ConditionalOnBean(ISysLogService.class)
	@Bean
	public SystemLogFilter systemLogFilter() {
		return new SystemLogFilter();
	}
	
	@Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        // cas登录服务器地址前缀
        myShiroRealm.setCasServerUrlPrefix(casServerUrlPrefix);
        // 客户端回调地址，登录成功后的跳转地址(自己的服务地址)
        myShiroRealm.setCasService(shiroServerUrlPrefix+casFilterUrlPattern);
        return myShiroRealm;
    }
	
	@Bean
	public DefaultWebSessionManager defaultWebSessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); 
		sessionManager.setGlobalSessionTimeout(1000*60*30);
        sessionManager.setDeleteInvalidSessions(true);  
        sessionManager.setSessionValidationSchedulerEnabled(true);  
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
	}
	
	@Bean(name="cacheManager",destroyMethod =  "destroy")
	public EhCacheManager ehCacheManager() {
		EhCacheManager cacheManager = new EhCacheManager();
		cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
		return cacheManager;
	}




    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //权限控制
        securityManager.setRealm(myShiroRealm());
        //session管理
        securityManager.setSessionManager(defaultWebSessionManager());
        //cacheManager
        securityManager.setCacheManager(ehCacheManager());
        securityManager.setSubjectFactory(new CasSubjectFactory());
        return securityManager;
    }

}
